/* Copyright 2016 Michael Sladoje and Mike Schälchli. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

package ch.zhaw.facerecognitionlibrary.PreProcessor;

import android.content.Context;
import android.content.res.Resources;
import android.util.Log;

import org.opencv.core.Mat;
import org.opencv.core.Rect;

import java.util.ArrayList;
import java.util.List;

import ch.zhaw.facerecognitionlibrary.Helpers.Eyes;
import ch.zhaw.facerecognitionlibrary.Helpers.FaceDetection;
import ch.zhaw.facerecognitionlibrary.Helpers.PreferencesHelper;
import ch.zhaw.facerecognitionlibrary.PreProcessor.BrightnessCorrection.GammaCorrection;
import ch.zhaw.facerecognitionlibrary.PreProcessor.Contours.DifferenceOfGaussian;
import ch.zhaw.facerecognitionlibrary.PreProcessor.Contours.LocalBinaryPattern;
import ch.zhaw.facerecognitionlibrary.PreProcessor.Contours.Masking;
import ch.zhaw.facerecognitionlibrary.PreProcessor.ContrastAdjustment.HistogrammEqualization;
import ch.zhaw.facerecognitionlibrary.PreProcessor.StandardPostprocessing.Resize;
import ch.zhaw.facerecognitionlibrary.PreProcessor.StandardPreprocessing.Crop;
import ch.zhaw.facerecognitionlibrary.PreProcessor.StandardPreprocessing.EyeAlignment;
import ch.zhaw.facerecognitionlibrary.PreProcessor.StandardPreprocessing.GrayScale;
import ch.zhaw.facerecognitionlibrary.R;

/**
 * 预处理工厂
 */
public class PreProcessorFactory {
    private Context context;
    private PreferencesHelper preferencesHelper; // 配置
    private Resources resources;
    public enum PreprocessingMode { // 预处理模式
        DETECTION, // 检测
        RECOGNITION // 识别
    };
    private PreProcessor preProcessorRecognition; // 人脸识别预处理
    private PreProcessor preProcessorDetection; // 人脸检测预处理
    private List<Mat> images;
    public CommandFactory commandFactory;  // 命令工厂
    private FaceDetection faceDetection; // 人脸检测
    private boolean eyeDetectionEnabled;

    public PreProcessorFactory(Context context) {
        this.context = context;
        this.faceDetection = new FaceDetection(context);
        preferencesHelper = new PreferencesHelper(context);
        resources = context.getResources();
        eyeDetectionEnabled = preferencesHelper.getEyeDetectionEnabled();
        commandFactory = new CommandFactory(); // 命令工厂
        commandFactory.addCommand(resources.getString(R.string.grayscale), new GrayScale()); // 灰度
        commandFactory.addCommand(resources.getString(R.string.eyeAlignment), new EyeAlignment()); // 眼睛对齐
        commandFactory.addCommand(resources.getString(R.string.crop), new Crop()); // 剪裁
        commandFactory.addCommand(resources.getString(R.string.gammaCorrection), new GammaCorrection(preferencesHelper.getGamma())); // 伽马校正
        commandFactory.addCommand(resources.getString(R.string.doG), new DifferenceOfGaussian(preferencesHelper.getSigmas())); // 高斯差分
        commandFactory.addCommand(resources.getString(R.string.masking), new Masking()); // 遮罩
        commandFactory.addCommand(resources.getString(R.string.histogrammEqualization), new HistogrammEqualization()); // 直方图均衡
        commandFactory.addCommand(resources.getString(R.string.resize), new Resize()); // 调整
        commandFactory.addCommand(resources.getString(R.string.lbp), new LocalBinaryPattern()); // 本地二进制模式
    }

    /**
     * 裁剪图像 -- 会检测人脸
     * @param img
     * @return
     */
    public List<Mat> getCroppedImage(Mat img){
        images = new ArrayList<Mat>();
        images.add(img);
        preProcessorDetection = new PreProcessor(faceDetection, getCopiedImageList(img), context);
        preProcessorRecognition = new PreProcessor(faceDetection, images, context);

        try {
            List<String> listDetection = getPreprocessings(PreferencesHelper.Usage.DETECTION); // 获取检测预处理配置
            preprocess(preProcessorDetection, listDetection); // 检测预处理

            preProcessorRecognition.setFaces(PreprocessingMode.RECOGNITION); // 获取人脸并设置
            preProcessorRecognition = commandFactory.executeCommand(resources.getString(R.string.crop), preProcessorRecognition); // 裁剪图像
            if (eyeDetectionEnabled) {
                Eyes[] eyes = preProcessorRecognition.setEyes(); // 获取眼睛并设置
                if (eyes == null || eyes[0] == null){
                    return null;
                }
            }
            preProcessorRecognition.setImages(Resize.preprocessImage(preProcessorRecognition.getImages(), preferencesHelper.getFaceSize()));
        } catch (NullPointerException e){
            Log.d("getCroppedImage", "No face detected");
            return null;
        }
        return preProcessorRecognition.getImages();
    }

    /**
     * 根据指定预处理模式获取预处理后的图像 -- 会检测人脸
     * @param img 图像
     * @param preprocessingMode 预处理模式
     * @return
     * @throws NullPointerException
     */
    public List<Mat> getProcessedImage(Mat img, PreprocessingMode preprocessingMode) throws NullPointerException {
        images = new ArrayList<Mat>();
        images.add(img);
        preProcessorDetection = new PreProcessor(faceDetection, getCopiedImageList(img), context);
        preProcessorRecognition = new PreProcessor(faceDetection, images, context);

        try {
            List<String> listDetection = getPreprocessings(PreferencesHelper.Usage.DETECTION); // 获取检测预处理配置
            preprocess(preProcessorDetection, listDetection); // 检测预处理

            preProcessorDetection.setFaces(preprocessingMode); // 获取人脸并设置
            preProcessorRecognition.setFaces(preProcessorDetection.getFaces()); // 设置人脸
            preProcessorRecognition.setAngle(preProcessorDetection.getAngle()); // 设置人脸角度
            preProcessorRecognition = commandFactory.executeCommand(resources.getString(R.string.crop), preProcessorRecognition); // 裁剪图像

            if (eyeDetectionEnabled) {
                Eyes[] eyes = preProcessorRecognition.setEyes(); // 获取眼睛并设置
                if (eyes == null || eyes[0] == null){
                    return null;
                }
            }

            if (preprocessingMode == PreprocessingMode.RECOGNITION){
                List<String> listRecognition = getPreprocessings(PreferencesHelper.Usage.RECOGNITION); // 获取识别预处理配置
                preprocess(preProcessorRecognition, listRecognition); // 识别预处理
            }

        } catch (NullPointerException e){
            Log.d("getProcessedImage", "No face detected");
            return null;
        }
        if (preprocessingMode == PreprocessingMode.DETECTION){
            return preProcessorDetection.getImages();
        } else {
            return preProcessorRecognition.getImages();
        }
    }

    /**
     * 获取预处理配置
     * @param usage
     * @return
     */
    private List<String> getPreprocessings(PreferencesHelper.Usage usage){
        ArrayList<String> preprocessings = new ArrayList<String>();
        preprocessings.addAll(preferencesHelper.getStandardPreprocessing(usage));
        preprocessings.addAll(preferencesHelper.getBrightnessPreprocessing(usage));
        preprocessings.addAll(preferencesHelper.getContoursPreprocessing(usage));
        preprocessings.addAll(preferencesHelper.getContrastPreprocessing(usage));
        preprocessings.addAll(preferencesHelper.getStandardPostprocessing(usage));
        return preprocessings;
    }

    /**
     * 预处理
     * @param preProcessor
     * @param preprocessings
     */
    private void preprocess(PreProcessor preProcessor, List<String> preprocessings){
        for (String name : preprocessings){
            preProcessor = commandFactory.executeCommand(name, preProcessor);
        }
    }

    /**
     * 获取人脸进行识别
     * @return 返回人脸所在位置
     */
    public Rect[] getFacesForRecognition() {
        if(preProcessorRecognition != null){
            return preProcessorRecognition.getFaces();
        } else {
            return null;
        }
    }

    /**
     * 获取复制图像列表
     * @param img
     * @return
     */
    private List<Mat> getCopiedImageList(Mat img){
        List<Mat> images = new ArrayList<Mat>();
        Mat imgCopy = new Mat();
        img.copyTo(imgCopy);
        images.add(imgCopy);
        return images;
    }

    /**
     * 获取识别角度
     * @return
     */
    public int getAngleForRecognition(){
        return preProcessorRecognition.getAngle();
    }

    public void setCascadeClassifierForFaceDetector(String cascadeAssetName){
        faceDetection.setCascadeClassifierForFaceDetector(context, cascadeAssetName);
    }
}
